#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <vector>
#include <thread>
#include <filesystem>
#include <memory>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

namespace fs = std::filesystem;

#define Show_Img(src) do {\
    cv::imshow("DEBUG", src);\
    cv::waitKey();\
} while (0);

// 图片转视频
int FileToVideo(const char* filePath, const char* videoPath, int timLim = INT_MAX, int fps = 10) {
    unique_ptr<FILE, decltype(&fclose)> fp(fopen(filePath, "rb"), fclose);
    if (!fp) return 1;

    fseek(fp.get(), 0, SEEK_END);
    int size = ftell(fp.get());
    rewind(fp.get());
    unique_ptr<char[]> temp(new char[size]);
    fread(temp.get(), 1, size, fp.get());

    fs::create_directories("outputImg");
    Code::Main(temp.get(), size, "outputImg", "png", 1LL * fps * timLim / 1000);
    FFMPEG::ImagetoVideo("outputImg", "png", videoPath, fps, 10, 100000);
    fs::remove_all("outputImg");

    return 0;
}

// 视频转图片
int VideoToFile(const char* videoPath, const char* filePath) {
    fs::remove_all("inputImg");
    fs::create_directories("inputImg");
    bool isThreadOver = false;
    std::thread th([&] { FFMPEG::VideotoImage(videoPath, "inputImg", "jpg"); isThreadOver = true; });
    int precode = -1;
    vector<unsigned char> outputFile;
    bool hasStarted = false;
    bool ret = 0;

    for (int i = 1;; ++i) {
        printf("Reading Image %05d.jpg\n", i);
        string imgName = "inputImg/" + to_string(i) + ".jpg";
        unique_ptr<FILE, decltype(&fclose)> fp(fopen(imgName.c_str(), "rb"), fclose);

        if (!fp || isThreadOver) {
            puts("Failed to open the video, is the video incomplete?");
            ret = 1;
            break;
        }

        Mat srcImg = imread(imgName, 1), disImg;
        fclose(fp.get());

        if (ImgParse::Main(srcImg, disImg)) {
            continue;
        }
        ImageDecode::ImageInfo imageInfo;
        bool ans = ImageDecode::Main(disImg, imageInfo);
        if (ans || !hasStarted) {
            if (imageInfo.IsStart)
                hasStarted = true;
            continue;
        }

        if (precode == imageInfo.FrameBase)
            continue;

        if (((precode + 1) & UINT16_MAX) != imageInfo.FrameBase) {
            puts("Error, there is a skipped frame, there are some images parsed failed.");
            ret = 1;
            break;
        }

        printf("Frame %d is parsed!\n", imageInfo.FrameBase);
        precode = (precode + 1) & UINT16_MAX;
        outputFile.insert(outputFile.end(), imageInfo.Info.begin(), imageInfo.Info.end());

        if (imageInfo.IsEnd)
            break;
    }

    if (ret == 0) {
        th.join();
        printf("\nVideo Parse is success.\nFile Size:%lldB\nTotal Frame:%d\n", outputFile.size(), precode);
        unique_ptr<FILE, decltype(&fclose)> fp(fopen(filePath, "wb"), fclose);
        if (!fp) return 1;
        outputFile.push_back('\0');
        fwrite(outputFile.data(), sizeof(unsigned char), outputFile.size() - 1, fp.get());
        return ret;
    }

    return 1;
}

int main(int argc, char* argv[]) {
    constexpr bool type = false;
    if constexpr (type) {
        if (argc == 4)
            return FileToVideo(argv[1], argv[2], stoi(argv[3]));
        else if (argc == 5)
            return FileToVideo(argv[1], argv[2], stoi(argv[3]), stoi(argv[4]));
    } else {
        if (argc == 3)
            return VideoToFile(argv[1], argv[2]);
    }
    cout << "Argument error, please check your argument" << endl;
    return 1;
}